package com.simon.train.business.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.simon.train.business.domain.*;
import com.simon.train.business.dto.ConfirmOrderQueryDto;
import com.simon.train.business.dto.ConfirmOrderSaveDto;
import com.simon.train.business.dto.ConfirmOrderTicketDto;
import com.simon.train.business.enums.ConfirmOrderStatusEnum;
import com.simon.train.business.enums.SeatColEnum;
import com.simon.train.business.enums.SeatTypeEnum;
import com.simon.train.business.mapper.ConfirmOrderMapper;
import com.simon.train.business.mapper.DailyTrainTicketMapper;
import com.simon.train.business.vo.ConfirmOrderQueryVO;
import com.simon.train.common.context.LoginMemberContext;
import com.simon.train.common.exception.BusinessException;
import com.simon.train.common.exception.BusinessExceptionEnum;
import com.simon.train.common.util.SnowUtil;
import com.simon.train.common.vo.PageVO;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class ConfirmOrderService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfirmOrderService.class);

    @Resource
    private ConfirmOrderMapper confirmOrderMapper;

    @Resource
    private DailyTrainTicketMapper dailyTrainTicketMapper;

    @Resource
    private DailyTrainCarriageService dailyTrainCarriageService;

    @Resource
    private DailyTrainSeatService dailyTrainSeatService;

    @Resource
    private AfterConfirmTicketService afterConfirmTicketService;

    public void SaveConfirmOrder(ConfirmOrderSaveDto confirmOrderSaveDto){
        ConfirmOrder confirmOrder = BeanUtil.copyProperties(confirmOrderSaveDto, ConfirmOrder.class);
        if (ObjectUtil.isNull(confirmOrder.getId())){
            //对于首次保存 也就是新增 需要对字段单独做处理
            confirmOrder.setId(SnowUtil.getSnowflakeNextId());
            DateTime time = DateTime.now();
            confirmOrder.setCreateTime(time);
            confirmOrder.setUpdateTime(time);
            confirmOrderMapper.insert(confirmOrder);
        } else {
            confirmOrder.setUpdateTime(DateTime.now());
            confirmOrderMapper.updateByPrimaryKey(confirmOrder);
        }

    }

    public PageVO<ConfirmOrderQueryVO> QueryConfirmOrderList(ConfirmOrderQueryDto confirmOrderQueryDto){
        ConfirmOrderExample confirmOrderExample = new ConfirmOrderExample();
        confirmOrderExample.setOrderByClause("id desc");
        ConfirmOrderExample.Criteria criteria = confirmOrderExample.createCriteria();
        LOG.info("当前请求的页码：{}", confirmOrderQueryDto.getPage());
        LOG.info("每页条数：{}", confirmOrderQueryDto.getPageSize());

        PageHelper.startPage(confirmOrderQueryDto.getPage(),confirmOrderQueryDto.getPageSize());
        List<ConfirmOrder> confirmOrderList = confirmOrderMapper.selectByExample(confirmOrderExample);

        PageInfo<ConfirmOrder> pageInfo = new PageInfo<>(confirmOrderList);
        LOG.info("总行数：{}", pageInfo.getTotal());
        LOG.info("总页数：{}", pageInfo.getPages());

        List<ConfirmOrderQueryVO> lists = BeanUtil.copyToList(confirmOrderList, ConfirmOrderQueryVO.class);
        PageVO<ConfirmOrderQueryVO> pageVO = new PageVO<>();
        pageVO.setList(lists);
        pageVO.setTotal(pageInfo.getTotal());

        return pageVO;
    }

    public void deleteConfirmOrder(Long id) {
        confirmOrderMapper.deleteByPrimaryKey(id);
    }



    /**
     * step1:选座逻辑：sell:10001 一共有6站（第一站站序规定为0） 当要购买2-5站时，对应的选项为中间的000  就要求中间为000 此时符合就可以售卖 只要有1就表明已经存在购买，就不可以出售
     * step2:同时选中后还需要计算购票后的sell 比如原来是10001 购买2-5站 然后售卖信息01110 之后按位或 得到11111
     * 同时需要注意：为了和函数调用等相互契合，这里我们都默认第一站的站序为0
     * 南京-上海-北京-西安  四个站 售卖情况用000三个数表示即可 规定站序从0开始则 南京0 上海1 北京2 西安3
     * 比如南京到北京一开始表示为00 总的座位为000  比较的时候startIndex为0 endIndex为2 则从000中截取substring(0,2) 则截取到[0,2) 也就是00作为substring
     * 那么currentSell经过替换就变成了11 那么fillBefore对11补0 长度为2 已经够了 再用fillAfter对11补0 长度为3 则为110 最终currentSell为110
     * 之后在通过 或运算 110|000 -> 110 再补长度到3无变化 最终售卖完就是110
     * @param trainSeat
     * @param startIndex
     * @param endIndex
     * @return
     */
    public boolean checkSell(DailyTrainSeat trainSeat, Integer startIndex, Integer endIndex){
        String sell = trainSeat.getSell();
        String substring = sell.substring(startIndex, endIndex);
        if(Integer.parseInt(substring) > 0){ //转成整型 大于0则说明存在1的情况 也就是有售卖
            LOG.info("座位{}在本次车站区间{}-{}已售过票，不可以选择该座位！"
                    ,trainSeat.getCarriageSeatIndex(), startIndex, endIndex);
            return false;
        } else {
            LOG.info("座位{}在本次车站区间{}-{}未售过票，可以选择该座位！"
                    ,trainSeat.getCarriageSeatIndex(), startIndex, endIndex);
            String currentSell = substring.replace('0', '1');
            currentSell = StrUtil.fillBefore(currentSell, '0', endIndex);
            currentSell = StrUtil.fillAfter(currentSell, '0', sell.length());

            int tmp_sell = NumberUtil.binaryToInt(sell) | NumberUtil.binaryToInt(currentSell);
            String new_sell = NumberUtil.getBinaryStr(tmp_sell);
            new_sell = StrUtil.fillBefore(new_sell, '0', sell.length());

            LOG.info("座位{}被选中，原售票信息为：{}，车站区间为{}-{},即{}  售卖后当前售卖信息为{}",
                    trainSeat.getCarriageSeatIndex(), sell, startIndex, endIndex, currentSell, new_sell);
            trainSeat.setSell(new_sell);
            return true;
        }
    }

    /**
     * 获取座位信息
     * 如果有座位则一次性挑选完毕 如果没有座位则一个一个挑选
     * @param date  列车日期
     * @param trainCode  列车编号
     * @param seatType  列车座位类型
     * @param column  座位的列号（选座情况下用于记录第一个被选定的座位的列）
     * @param offsetList  相对偏移（用于在选座情况下 只通过一次循环就查完所有座位）
     */
    public boolean getSeat(List<DailyTrainSeat> FinalSeatList, Date date, String trainCode, String seatType,
                        String column, List<Integer> offsetList, Integer startIndex, Integer endIndex){
        //1.先搞清楚当前是几等车厢 然后通过循环获取到对应的车厢
        //2.再通过循环获取到对应的座位数据
        List<DailyTrainCarriage> carriages = dailyTrainCarriageService.selectBySeatType(date, trainCode, seatType);
        LOG.info("本次查询到的符合条件的车厢数量为:{}", carriages.size());
        /**
         * 之所以定义在外面的逻辑思考 具体来说由于getSeat函数在针对选座和不选座的处理不同
         * 1.不选座:getSeat函数只会选择一个座位 多张票的情况是通过循环调用getSeat实现的 所以TempFinalSeatList甚至可以不用 找到了直接写到FinalSeatList都行 所以放哪里 清空不清空都无所谓
         * 2.1 选座+同车厢找座位找到:这种情况限定了选座一定会在同车厢找到座位 那么TempFinalSeatList可以定义在for循环里
         *     2.1.1
         *     由于第二个for循环（for (DailyTrainSeat seat: trainSeats) {）会先找到一个位置 然后在它里面再循环找到其它选座的相对位置的座位
         *     所以seat变化就等于找座位的第一个变化了 所以TempFinalSeatList可以定义在seat的循环中 此时seat变化就会自动清空TempFinalSeatList 因而车厢变化也无需清空 => 此时就无需清空
         *     2.1.2
         *     至于车厢的循环 如果把TempFinalSeatList定义在车厢那个for循环下 那么车厢变化自然就不用清空它了 但是座位变化就需要在if(!IsChooseAllSeat)位置清空TempFinalSeatList
         *     避免在选座的情况下 上一轮seat没选择成功却没清空 导致影响本轮
         * 2.2 BUG情况(现实情况) 真实情况下即使是选座 也未必可以成功 那么就需要针对选座不成功的情况做处理 现实情况会出现选座不成功而分配 从而导致不在一个车厢的情况
         *     不管后续到底是如何处理这种情况 我们需要考虑到不同的车厢也需要共享TempFinalSeatList的情况
         * => 所以我们把TempFinalSeatList定义在两个for循环外面 同时在座位发生变化的时候把TempFinalSeatList清空 目前来说车厢选座不够的情况也会跳出第二个for循环从而换车厢 此时TempFinalSeatList刚被清空完
         * TODO: web用户端 确定下单并且成功以后，记得设置好用户页面的成功跳转
         */
        List<DailyTrainSeat> TempFinalSeatList = new ArrayList<>();

        /**
         * 根据座位类型设置标志位
         * 一等座的每行为4个座位 二等座每行为5个座位 通过设置flag 可以在有选座的情况首个座位不符合条件的时候进行快速的跳转 无需一个一个遍历 极大的提高了效率
         */
        int SeatTypeFlag = 0;
        if(seatType.equals("1")) SeatTypeFlag = 4;
        else SeatTypeFlag = 5;
        boolean chooseSeatFlag = false;
        if(StrUtil.isNotBlank(column)) chooseSeatFlag = true;
        /**
         * 根据座位类型去找枚举类 获取对应的枚举数组 后续可以用于计算快速选座的偏移
         */
        List<SeatColEnum> SeatTypeEnumList = SeatColEnum.getColsByType(seatType);
        int colListIndex = SeatColEnum.getColListIndex(SeatTypeEnumList, column);

        //一个车厢一个车厢的遍历获取座位数据
        for (DailyTrainCarriage carriage: carriages) {
            TempFinalSeatList = new ArrayList<>();
            LOG.info("本次选座的车厢号是:{}", carriage.getIndex());
            List<DailyTrainSeat> trainSeats = dailyTrainSeatService.selectByDailyTrainCarriageIndex(date, trainCode, seatType, carriage.getIndex());
            LOG.info("车厢{}的座位数量是:{}", carriage.getIndex(), trainSeats.size());

            boolean IndexOverCarriage = false;
            int stepLength = 1;
            int mark = 0;
            if(chooseSeatFlag){ //如果为选座的情况 我们针对性的去处理选座的初始mark和相应的步长 避免一直挨个循环判断
                mark = colListIndex; stepLength = SeatTypeFlag;
            }
            for(; mark < trainSeats.size(); mark=mark+stepLength){
//            for (DailyTrainSeat seat: trainSeats) {
                if(IndexOverCarriage) break;

                String seatCol = trainSeats.get(mark).getCol();
                Integer carriageSeatIndex = trainSeats.get(mark).getCarriageSeatIndex();

                /**
                 * 修复BUG：未选座的情况 避免重复选票情况的出现
                 * 光break跳出本for是不够的 还需要跳出本次seat循环 进入下一个seat的循环
                 */
                Boolean Is_this_seat_chosen = false;
                for (DailyTrainSeat seats: FinalSeatList) {
                    if(seats.getId().equals(trainSeats.get(mark).getId())){
                        LOG.info("本次欲购买的座位{} 已经在选票座位列表被选择过了，跳过该车票！",trainSeats.get(mark).getCarriageSeatIndex());
                        Is_this_seat_chosen = true;
                        break;
                    }
                }
                if(Is_this_seat_chosen) continue;

                //判断column 如果有值的话 需要对比列号
                if(chooseSeatFlag){
                    LOG.info("本次选择的座位列号是：{}", column);
                    if(!column.equals(seatCol)){
                        LOG.info("本次座位号是：{},需要的座位号是：{}，不符合选座条件，将循环下一个座位",seatCol, column);
                        continue;
                    }
                }else LOG.info("本次没有选择座位，直接循环所有座位即可");

                //针对每一个座位进行循环 判断该座位的售卖区间是否可卖
                Boolean isSell = checkSell(trainSeats.get(mark), startIndex, endIndex);
                if(isSell){
                    LOG.info("该座位可卖，选中座位成功！");
                    TempFinalSeatList.add(trainSeats.get(mark));
                    if(CollUtil.isEmpty(offsetList)){
                        LOG.info("本次并非选座，该座位选择完毕！");
                        FinalSeatList.addAll(TempFinalSeatList);
                        return true;
                    }
                }else{
                    LOG.info("该座位不可售！");
                    continue;
                }

                /**
                 * 前面的逻辑主要就是在选择第一个座位，没有选座的本身就要循环去get座位，那么选中一个座位就跳出这个函数了
                 * 而有选座的情况先会根据column选择第一个座位，然后才会根据offsetList偏移来继续处理接下来的部分
                 *
                 * 根据offset选择剩下的座位  此时为选座的情况 根据offsetList继续进行下一个选座
                 * 由于offsetList第一个为0，而第一个座位已经选好了，我们直接从1开始，选择下一个目标偏移
                 */
                Boolean IsChooseAllSeat = true;
                for(int i =1; i < offsetList.size(); i++){
                    Integer NextSeatIndex = carriageSeatIndex + offsetList.get(i);

                    if(NextSeatIndex-1 >= trainSeats.size()){
                        LOG.info("此时选座偏移列表的下标已经越界，说明当前车厢已经不存在可以满足该偏移的选座了，需要考虑另外的车厢！");
                        /**
                         * IndexOverCarriage标志 在循环开始设置为false 当出现某个座位循环offsetList越界 即使换下一个座位也一定越界
                         * 则可以通过标志设置为true 直接终止本次关于seat的循环 直接换一个车厢去新的循环
                         */
                        IndexOverCarriage = true;
                        IsChooseAllSeat = false;
                        break;
                    }

                    //CarriageSeatIndex在数据库中是从1开始逐步递增的
                    //而最开始的for (DailyTrainSeat seat: trainSeats)循环是从trainSeats的下标0开始的
                    //所以我们获取的话需要记得减1
                    boolean checkSell = checkSell(trainSeats.get(NextSeatIndex - 1), startIndex, endIndex);
                    if(!checkSell){
                        LOG.info("本次想选择的座位列是：{},座位下标是：{}，但是目标座位已售，不可选！即将更换另一个座位作为选座的初始目标！"
                                ,trainSeats.get(NextSeatIndex - 1).getCol(),
                                NextSeatIndex);
//                        mark = mark + SeatTypeFlag;
                        IsChooseAllSeat = false;
                        break;
                    }else{
                        LOG.info("本次选座成功！该目标座位列是：{}，座位下标是：{}，选座成功！"
                                ,trainSeats.get(NextSeatIndex - 1).getCol()
                                ,NextSeatIndex);
                        TempFinalSeatList.add(trainSeats.get(NextSeatIndex - 1));
                    }
                }
                if(!IsChooseAllSeat){
                    LOG.info("本次存在选座情况，但选座不符合条件！进入下一个座位的初始选择！");
                    TempFinalSeatList = new ArrayList<>(); //清空 必须定义的时候是在两个for循环的外面 所以如果定义在for循环里面 那也可以不清空 每次重新定义就行
                    continue;
                }

                //此时所有座位都选择成功 可以开始保存
                FinalSeatList.addAll(TempFinalSeatList);
                return true;

            }
        }
        LOG.info("本次已经循环了所有的车厢和所有的座位，余票数量不为0的情况下不会出现购票失败的可能！说明选座并没有符合条件的座位，则默认进入非选座的处理逻辑进行购票！");
        return false;

    }

    public void ConfirmTicket(ConfirmOrderSaveDto confirmOrderSaveDto){
        //1.前端会对数据做第一次校验 为了避免黑客绕过前端直接访问接口，confirmOrderSaveDto会第二次通用合法性校验 这里是第三次 也就是业务合法校验
        //车次编号是否存在 余票是否存在 车次是否在有效期内 tickets条数要大于0 同乘客同车次是否已经买过
        //便于学习 这里就不实现了 很简单的校验

        //2. 保存确认订单 放入数据库 状态设置为初始
        ConfirmOrder confirmOrder = new ConfirmOrder();
        confirmOrder.setId(SnowUtil.getSnowflakeNextId());
        confirmOrder.setMemberId(LoginMemberContext.getId());
        confirmOrder.setDate(confirmOrderSaveDto.getDate());
        confirmOrder.setTrainCode(confirmOrderSaveDto.getTrainCode());
        confirmOrder.setStart(confirmOrderSaveDto.getStart());
        confirmOrder.setEnd(confirmOrderSaveDto.getEnd());
        confirmOrder.setDailyTrainTicketId(confirmOrderSaveDto.getDailyTrainTicketId());
        confirmOrder.setStatus(String.valueOf(ConfirmOrderStatusEnum.INIT.getCode()));
        DateTime time = DateTime.now();
        confirmOrder.setCreateTime(time);
        confirmOrder.setUpdateTime(time);
        List<ConfirmOrderTicketDto> tickets = confirmOrderSaveDto.getTickets();
        LOG.info(tickets.toString());
        LOG.info(JSON.toJSONString(tickets));
        confirmOrder.setTickets(JSON.toJSONString(tickets));
        confirmOrderMapper.insert(confirmOrder);

        //3.查出余票记录 看是否还存在余票
        DailyTrainTicketExample dailyTrainTicketExample = new DailyTrainTicketExample();
        DailyTrainTicketExample.Criteria criteria = dailyTrainTicketExample.createCriteria();
        criteria.andStartEqualTo(confirmOrder.getStart());
        criteria.andEndEqualTo(confirmOrder.getEnd());
        criteria.andDateEqualTo(confirmOrder.getDate());
        criteria.andTrainCodeEqualTo(confirmOrder.getTrainCode());
        List<DailyTrainTicket> dailyTrainTickets = dailyTrainTicketMapper.selectByExample(dailyTrainTicketExample);

        Integer ydz = 0; Integer edz = 0; Integer rw = 0; Integer yw = 0;
        if(CollUtil.isNotEmpty(dailyTrainTickets)){
            DailyTrainTicket dailyTrainTicket = dailyTrainTickets.get(0);
            ydz = dailyTrainTicket.getYdz();
            edz = dailyTrainTicket.getEdz();
            rw = dailyTrainTicket.getRw();
            yw = dailyTrainTicket.getYw();
            LOG.info("查出余票记录：{}",dailyTrainTicket);
        }

        DailyTrainTicket dailyTrainTicket = dailyTrainTickets.get(0);

        //4.预先扣减余票数量 看库存是否充足
        for (ConfirmOrderTicketDto ticket: confirmOrderSaveDto.getTickets()) {
            SeatTypeEnum seatTypeEnum = EnumUtil.getBy(SeatTypeEnum::getCode, ticket.getSeatTypeCode());
            switch (seatTypeEnum){
                case YDZ -> {
                    if(ydz <= 0) throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_YDZ_ERROR);
                    ydz = ydz -1;
                }
                case EDZ -> {
                    if(edz <= 0) throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_EDZ_ERROR);
                    edz = edz -1;
                }
                case YW -> {
                    if(yw <= 0) throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_YW_ERROR);
                    yw = yw -1;
                }
                case RW -> {
                    if(rw <= 0) throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_RW_ERROR);
                    rw = rw -1;
                }
            }
        }


        Date date = confirmOrder.getDate();
        String TrainCode = confirmOrder.getTrainCode();

        //5.选座的核心逻辑
        /**
         * 1.通过循环 针对目标车厢的类别 一个车厢一个车厢的遍历获取座位数据
         * 2.每个座位也通过循环去遍历 看座位是否满足选座需求
         * 3.选择符合条件的座位 如果车厢不满足则下一个车厢 （多个选座应该在同一个车厢）
         */
        //偏移值的计算(有选座的情况下才需要计算) 计算相对于第一个座位的偏移值
        //比如选择的是C1,D2 则偏移为[0,5] 比如选择的是A1,B1,C1 则偏移值是[0,1,2]
        ConfirmOrderTicketDto tempTicket = confirmOrderSaveDto.getTickets().get(0);
        List<DailyTrainSeat> FinalSeatList = new ArrayList<>();
        if(StrUtil.isNotBlank(tempTicket.getSeat())){
            LOG.info("本次购票存在选座,即将计算偏移！");
            //首先需要确定具体是什么类型的座位 一等座和二等座的型号不同
            List<SeatColEnum> columnList = SeatColEnum.getColsByType(tempTicket.getSeatTypeCode()); //如columnList={A,C,D,F}

            //接下来通过循环拼接出两行如 A1,B1,D1,F1,A2,B2,D2,F2这种
            List<String> SeatListArray = new ArrayList<>();
            for(int i=1; i<=2; i++)
                for (SeatColEnum seatColEnum : columnList)
                    SeatListArray.add(seatColEnum.getCode() + i);
            LOG.info("得到的两排座位情况为:{}",SeatListArray);

            //接下来开始计算偏移值 我们可以先计算出目标的绝对索引位置 之后再计算差值再处理 比如A1和C2 在数组的绝对下标为[1,6] 之后再计算第一个的差值为1-0=1 那么真正的偏移为[1-1,6-1]
            List<Integer> absoluteList = new ArrayList<>();
            List<Integer> offsetList = new ArrayList<>();
            for (ConfirmOrderTicketDto confirmOrderTicketDto: confirmOrderSaveDto.getTickets()) {
                int absoluteIndex = SeatListArray.indexOf(confirmOrderTicketDto.getSeat());
                absoluteList.add(absoluteIndex);
            }
            LOG.info("所有座位的绝对偏移为：{}", absoluteList);

            for (Integer absoluteIndex: absoluteList) {
                Integer offset = absoluteIndex - absoluteList.get(0);
                offsetList.add(offset);
            }
            LOG.info("所有座位的相对偏移为：{}", offsetList);

            boolean flag = getSeat(FinalSeatList, date, TrainCode, tempTicket.getSeatTypeCode(),
                    tempTicket.getSeat().split("")[0], offsetList,
                    dailyTrainTicket.getStartIndex(), dailyTrainTicket.getEndIndex());
            if(!flag){ //说明选座并没有满足 则进入非选票的情况处理
                LOG.info("此时选座并没有成功被满足，默认进入非选座处理逻辑！");
                for (ConfirmOrderTicketDto ticket: tickets) {
                    getSeat(FinalSeatList, date, TrainCode, ticket.getSeatTypeCode(), null, null,
                            dailyTrainTicket.getStartIndex(), dailyTrainTicket.getEndIndex());
                }
            }


        }else{
            LOG.info("本次购票不存在选座,无需计算偏移！");
            /**
             * 无选座的情况不会出现选座不能满足的可能，因为余票一定是足够的 所以getSeat一定返回true 无需接收返回值
             */
            for (ConfirmOrderTicketDto ticket: tickets) {
                getSeat(FinalSeatList, date, TrainCode, ticket.getSeatTypeCode(), null, null,
                        dailyTrainTicket.getStartIndex(), dailyTrainTicket.getEndIndex());
            }
        }
        LOG.info("最终选座情况：{}", FinalSeatList);


        //6.座位选中后 开始进行事务处理
        /**
         * 1.座位表的售卖情况
         * 2.余票详情表的余票数量
         * 3.为会员增加购票记录（目前还没添加这个表）
         * 4.更新 确认订单表 对应的状态为 成功
         *
         * 由于需要事务处理 必须在不同的类 这里我们重新定义一个service进行事务处理
         */
        afterConfirmTicketService.AfterConfirmTicket(dailyTrainTicket, FinalSeatList, confirmOrderSaveDto.getTickets(), confirmOrder);

    }
}
